#requires -version 2.0 
function Get-NaAggr {
    <#
    .Synopsis
        Get aggregate status.
    .Description
           List of aggregates and their status. 
    .Parameter Aggregate
           The aggregate to get status for. If not supplied, get status of all aggregates. 
    .Parameter Server
        NaServer to query
    .Example
        Get the status of all aggregates.
           PS > Get-NaAggr
    .Example
        Get the status of aggregate aggr0.
           PS > Get-NaAggr -name aggr0
    .Outputs
        NetApp.SDK.NaAggr[]
    .Link
        Set-NaAggr
        Remove-NaAggr
        Rename-NaAggr
    #> 
    [CmdletBinding(SupportsShouldProcess=$FALSE,SupportsTransactions=$False,ConfirmImpact="None",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="The aggregate to get status for.")]
        [Alias("Aggregate")]
        [STRING]
        $Name,
    
        [Parameter(Position=1,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object Netapp.Manage.NaElement -ArgumentList @("aggr-list-info")
        $request.AddNewChild("verbose",$TRUE)
        if ($Name){
           $request.AddNewChild("aggregate",$Name)
        }
		try {
        	$result = ([xml]$server.InvokeElem($request)).results
		}
		catch [NetApp.Manage.NaApiFailedException]{
			Write-Warning $_.Exception.GetBaseException().message
			$result = $null
			continue;
		}
		if ($?){
			$result.aggregates."aggr-info" | % {        
			$p = new-object Collections.Hashtable
				if (!($_.state -eq "creating")){
					$_.plexes."plex-Info" | % { 
						$rg = new-object Collections.Hashtable
						$_."raid-groups"."raid-group-info" |% {
							$rg.add(($_.Name.split('/')[3]), (New-Object NetApp.SDK.NaRaidGroup -ArgumentList @(
								"/vol$($_.Name)", 
								(ConvertFrom-NaBool $_.Reconstructing),
								[array]($_.disks."disk-info"|%{$_.name}),
								$(if ($_."reconstruction-percentage" -eq $null){0}else{$_."reconstruction-percentage"}))
							))
						}
						$p.add(($_.name.split('/')[2]), (New-Object NetApp.SDK.NaPlex -ArgumentList @(
							"/vol$($_.name)", 
							(ConvertFrom-NaBool $_."is-online"), 
							(ConvertFrom-NaBool $_."is-resyncing"),
							$RG,
							$(if ($_."resyncing-percentage" -eq $null){0}else{$_."resyncing-percentage"}))
						))
					}
				}
				return New-Object NetApp.SDK.NaAggr -ArgumentList @(
					$_."checksum-status",
					$_."checksum-style",
					$_."disk-count",
					$_."files-total",
					$_."files-used",
					(ConvertFrom-NaBool $_."has-local-root"),
					(ConvertFrom-NaBool $_."has-partner-root"),
					(ConvertFrom-NaBool $_."is-checksum-enabled"),
					(ConvertFrom-NaBool $_."is-inconsistent"),
					(ConvertFrom-NaBool $_."is-snaplock"),
					$_."mirror-status",
					$_."name",
					"/vol/$($_.name)",
					$_."plex-count",
					$p,
					$_."raid-size",
					$_."raid-status",
					[int64]$_."size-available",
					[int64]$_."size-total",
					[int64]$_."size-used",
					$_."state",
					$_."type",
					$_."uuid",
					$_."volume-count",
					[array]($_.volumes."contained-volume-info" | % { $_.name }),
					$_."size-percentage-used",
					$_."snaplock-type",
					$_."volume-count-striped-dv",
					$_."volume-count-striped-mdv"
				)
			}
		}
    }
}
Function Rename-NaAggr {
    <#
    .Synopsis
        Rename the specified aggregate.
    .Description
        Rename the specified aggregate.
    .Parameter Name
          Name of the aggregate to rename.
    .Parameter NewName
           The new name desired for the aggregate.
    .Parameter Server
        NaServer to query
    .Example
        # rename aggr0 to aggr1
           PS > Rename-NaAggr -Name aggr0 -NewName aggr1
    .Example
        # rename aggr1 to aggr2
           PS > Get-NaAggr aggr1 | Rename-NaAggr -NewName aggr2
    .Outputs
        NetApp.SDK.NaAggr[]
    .Link
        Get-NaAggr
        Set-NaAggr
        Remove-NaAggr
    #> 
    [CmdletBinding(SupportsShouldProcess=$true,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the aggregate to rename.")]
        [Alias("Aggregate")]
        [STRING]
        $Name,
    
        [Parameter(Position=1,Mandatory=$TRUE,HelpMessage="The new name desired for the aggregate.")]
        [string]
        $NewName, 
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    Process{
        $request = New-Object Netapp.Manage.NaElement -ArgumentList @("aggr-rename")
        $request.AddNewChild("aggregate",$name)
        $request.AddNewChild("new-aggregate-name",$newName)
        if ($pscmdlet.ShouldProcess($Server.Server,"rename $name to $NewName")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
	            return Get-NaAggr -Name $NewName -Server $server
			}
        }
    } 
}
Function Set-NaAggr {
    <#
    .Synopsis
        Used to Set the State of an aggregate/traditional volume.
    .Description
        Used to Set the State of an aggregate/traditional volume.
    .Parameter Name
           Name of the aggregate.
    .Parameter Offline
           Take the specified aggregate or plex offline. The operation takes effect before the API returns. Except in
         maintenance mode, the current root aggregate may not be taken offline. An aggregate marked to become the 
         root aggregate cannot be taken offline. An aggregate containing one or more flexible volumes cannot be taken
         offline; its contained volumes must first be destroyed. A number of operations being performed on the given
         aggregate can prevent this operation from succeeding, either at all or for various lengths of time. if such
         operations are found, the system waits up to one second for them to finish. if they don't, the command is 
         aborted. A check is also made for files in the aggregate opened by internal ONTAP processes. The command is
         aborted if any are found.
    .Parameter Online
           Bring the specified aggregate or plex online. This command takes effect immediately. All volumes on the aggregate
         are brought to whatever state they were in before the aggregate was restricted or taken offline. if there are CIFS
         shares associated with the any of the aggregate's volumes that were also onlined, they are enabled.
    .Parameter KeepUUID
           Keep existing foreign raidtree id (aggregate uuid). Normally when a foreign raidtree is onlined, the raidtree rtid
        (aggregate uuid) is changed to guarantee uniqueness. The behavior is overridden when this option is specified.
    .Parameter Restrict
           Restrict the specified aggregate. If the aggregate is embedded in a traditional volume, the entire traditional 
         volume will be restricted. An aggregate with one or more flexible volumes cannot be restricted; all of its 
         contained volumes must first be destroyed.
    .Parameter CifsDelay
           The number of seconds to wait before offlining/restricting any volumes that are hosted in the given aggregate
         that have active CIFS shares (if any). A value of 0 means that all such volumes are to be offlined/restricted
         immediately with no warning. CIFS users can lose data if they are not given a chance to terminate applications
         gracefully. By default, CifsDelay is 0. 

         NOTE: This argument may ONLY appear if the -UnmountVolumes argument is also specified.
    .Parameter UnmountVolumes
           This option specifies that all of the volumes hosted by the given aggregate are to be unmounted before the 
         offline/restrict operation is executed. By default, the system will reject any attempt to offline/restrict
         an aggregate that hosts one or more online volumes. 
    .Parameter Server
        NaServer to query
    .Example
        # Offline aggr1 
        PS > Set-NaAggr -Name aggr1 -offline
    .Example
        # restrict aggr1 forcefully unmounting any existing volumes 
        PS > Set-NaAggr -Name aggr1 -Restrict -UnmountVolumes
    .Example
        # Online any restricted/offlined aggregates 
        Get-NaAggr | ? {$_.State -ne "online"} | Set-NaAggr -Online
    .Outputs
        NetApp.SDK.NaAggr[]
    .Link
        Get-NaAggr
        Rename-NaAggr
        Remove-NaAggr
    #>
    [CmdletBinding(SupportsShouldProcess=$true,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="online")]
    param(
        [Parameter(ParameterSetName="online",Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the Aggregate ")]
        [Parameter(ParameterSetName="offline",Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the Aggregate ")]
        [Parameter(ParameterSetName="restrict",Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the Aggregate ")]
        [Alias("Aggregate")]
        [string]
        $name,
    
        [Parameter(ParameterSetName="offline")]
        [switch]
        $offline,
    
        [Parameter(ParameterSetName="online")]
        [switch]
        $online,
    
        [Parameter(ParameterSetName="online")]
        [switch]
        $KeepUUID,
    
        [Parameter(ParameterSetName="restrict")]
        [switch]
        $Restrict,
        
        [Parameter(ParameterSetName="offline",HelpMessage="The number of seconds to wait before offlining/restricting any volumes that are hosted in the given aggregate")]
        [Parameter(ParameterSetName="restrict",HelpMessage="The number of seconds to wait before offlining/restricting any volumes that are hosted in the given aggregate")]
        [int]
        $CifsDelay,
    
        [Parameter()]
        [switch]
        $unmountVolumes,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    Process{
        switch ($PSCmdlet.ParameterSetName){
            "online" 
            {
                $request = New-Object Netapp.Manage.NaElement -ArgumentList @("aggr-online")
                if ($KeepUUID){
                    $request.AddNewChild("keep-same-uuid",$TRUE)
                }
                $msg = "Online aggregate"
            }
            "offline"
            {
                $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-offline")
                if ($unmountVolumes){ 
                    $request.AddNewChild("unmount-volumes",$TRUE)
                    if ($CifsDelay){ 
                        $request.AddNewChild("cifs-delay-seconds",$CifsDelay)
                    }
                }
                $msg = "Offline aggregate"
            }
            "Restrict"
            { 
                $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-restrict")
                if ($unmountVolumes){ 
                    $request.AddNewChild("unmount-volumes",$TRUE)
                    if ($CifsDelay){
                        $request.AddNewChild("cifs-delay-seconds",$CifsDelay)
                    }
                }
                $msg = "Restrict aggregate"
            }
        }
        $request.AddNewChild("aggregate",$Name)
        if ($pscmdlet.ShouldProcess($Name,$msg)){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				$result = $null
				continue;
			}
			if ($?){
            	return Get-NaAggr -Name $Name -server $server 
			}
        }
    } 
}
Function Remove-NaAggr {
    <#
    .Synopsis
        Destroys the specified aggregate or plex.
    .Description
        Destroys the specified aggregate or plex. If an aggregate is specified,
        all plexes in the aggregate are destroyed. if the aggregate is embedded
        in a traditional volume, then the entire traditional volume is destroyed.
        Otherwise, the operation will be rejected if there are one or more flexible
        volumes in the aggregate - they must be destroyed beforehand. if a plex is
        specified, only that plex is destroyed, leaving an unmirrored aggregate 
        containing the remaining plex. The disks in the destroyed object become
        spare disks. Only offline aggregates and plexes can be destroyed. 
    .Parameter Name
        Name of the Aggregate to be destroyed.
    .Parameter Path
        Name of the existing aggregate or plex to be destroyed, using the following format: [/vol/][] 
    .Parameter Force
        When using Remove-NaAggr on a traditional volume, force the destruction of the volume even
        if a non-default vfiler has storage on it. Normally, the system will not destroy such a 
        volume and will instead return EVOLUME_HAS_VFILER_STORAGE. 
    .Parameter Server
        NaServer to query
    .Example
        # Destroy aggr1
        PS > Get-NaAggr aggr1 | Remove-NaAggr
    .Example
        # destroy plex1 from aggr3
        PS > $Aggr = Get-NaAggr aggr3
        PS > $Aggr.Plexes.Plex1 | Remove-NaAggr
    .Example
        # You can also just type in the path if you know it.
        PS > Remove-NaAggr -Path "/vol/vol1" -force -whatif
    .Link
        Get-NaAggr
        Set-NaAggr
        Rename-NaAggr
    #> 
    [CmdletBinding(SupportsShouldProcess=$true,SupportsTransactions=$False,ConfirmImpact="high",DefaultParameterSetName="byname")]
    param(
        
        [Parameter(Position=0,Mandatory=$TRUE,ParameterSetName='byname',HelpMessage="Name of the Aggregate to be destroyed.")]
        [Alias("Aggragate")]
        [string]
        $Name,
        
        [Parameter(Mandatory=$TRUE,ParameterSetName='byPath',ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the existing aggregate or plex to be destroyed, use Get-NaAggr to retrieve the path of a known aggr.")]
        [string]
        $Path,
    
        [Parameter()]
        [switch]
        $force,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        if ($Name){
            $Path = Get-NaAggr -Name $Name -Server $Server | Select-Object -ExpandProperty Path
        }
        $request = New-Object Netapp.Manage.NaElement -ArgumentList @("aggr-destroy")
        $request.AddNewChild("aggregate",$Path)
        if ($force){
            $request.AddNewChild("force",$TRUE)
        }
        if ($pscmdlet.ShouldProcess($Server.Server,"Destroying $path")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
        }
    } 
}
Function New-NaAggr {
    <#
    .Synopsis
        Add disks to the specified aggregate.
    .Description
        Create a new aggregate with the given name. Up to 200 aggregates can be created on each filer
        (including the aggregates that are "embedded" in traditional volumes, which must be created as
        a side effect of a traditional volume creation). The aggregate may not yet be operational 
        immediately after the API returns. Use Get-NaAggr to query the status of the newly-created
        aggregate to determine when it is fully operational. 

        NOTE: if ECANT_USE_ALL_DISKS is returned, then the requested aggregate was indeed created, just
        not with all the disks that were specified. 
    .Parameter Name
        Name of the aggregate to create. The aggregate name can contain letters, numbers, and the 
        underscore character(_), but the first character must be a letter or underscore. 
    .Parameter Disk
        Specific list of disks to use for the new aggregate. If the aggregate is mirrored and a specific       list of disks is supplied, another list -MirrorDisks must also be supplied with the same 
        number of disks. 
    .Parameter DiskCount
        Number of disks to place into the aggregate, including parity disks. The disks in this newly 
        created aggregate come from the spare disk pool. The smallest disks in this pool join the 
        aggregate first, unless the -DiskSize argument is provided.
    .Parameter DiskSize
        Disk size in GB. Disks that are within 10% of the specified size will be selected for use in the
        aggregate. if not specified, existing groups are appended with disks that are the best match for
        the largest disk in the group. When starting new groups, the smallest disks are selected first. 
    .Parameter DiskType
        Type of disks to use: ATA, EATA, FCAL, LUN, SAS, SATA, SCSI, XATA, or XSAS. 
        This option is needed only when disks of different types are connected to the filer. An aggregate can
        never be created using a mix of disks of different types. 
    .Parameter RPM
        Rotational speed of disks in revolutions per minute. Possible values are: 5400, 7200, 10000, and 15000.
        This option is needed only when disks with different rotational speeds are connected to the filer.
    .Parameter LanguageCode
        Specifies the language to use for the new traditional volume via a language code. The default language 
        is the one used by the filer's root volume. This option may be used only when creating a traditional 
        volume, i.e. when -Traditional 

        Available language codes are:     
        C (POSIX) 
        da (Danish)
        de (German)
        en (English)
        en_US (English (US)) 
        es (Spanish)
        fi (Finnish)
        fr (French)
        he (Hebrew)
        it (Italian)
        ja (Japanese euc-j) 
        ja_JP.PCK (Japanese PCK (sjis))
        ko (Korean)
        no (Norwegian)
        nl (Dutch)
        pt (Portuguese)
        sv (Swedish)
        zh (Simplified Chinese)
        zh.GBK (Simplified Chinese (GBK))
        zh_TW (Traditional Chinese euc-tw) 
        zh_TW.BIG5 (Traditional Chinese Big 5)
 
        NOTE: To use UTF-8 as the NFS character set, append '.UTF-8'. 
    .Parameter RaidSize
        Specifies the maximum number of disks in each RAID group in the aggregate. The maximum value for this
        parameter is 28. The default value is platform-dependent. The valid range of values is also 
        platform-dependent, but never wider than [2..28]. 
    .Parameter RaidType
        Specifies the type of RAID groups to use in the new aggregate. Possible values: raid4, raid_dp. The 
        default value is raid4 on most platforms. 
    .Parameter Force
        Disks in a plex are not normally permitted to span spare pools. This behavior is overridden with this option.
    .Parameter Mirrored
        Specifies that the new aggregate be mirrored (have two plexes). If -Mirrored is specified, then the indicated
        disks will be split across the two plexes. By default, the new aggregate will not be mirrored.
    .Parameter MirrorDisks
        Specific list of mirror disks needed to accompany the list in the -Disk parameter. This list must
        contain the same number of disks as specified in -Disk. 

        To create a list of disk use Get-NaDisk.  The following example will select 5 disks from shelf 2
        PS > $disks = Get-NaDisk | ? {$_.shelf -eq 2 -and $_.RaidState -eq "spare"} | select -first 5
    .Parameter SnapLock
        Specifies the creation of a SnapLock aggregate. By default, is-snaplock is not specified. When -Snaplock is specified
        the type of snaplock aggregate is determined in the following way:
         1> if -SnapLockType is set, create the type specified in -SnapLockType (see -SnapLockType for more details) 
         2> Otherwise, create a Snaplock enterprise aggregate if a Snaplock enterprise license has been installed. 
         3> Otherwise, create a Snaplock compliance aggregate. 
        NOTE: ESERVICENOTLICENSED is returned if the required Snaplock Compliance or Enterprise license is not installed. 
               EONTAPI_EWORMNOCLOCK is returned if SnapLock Compliance Clock is not running. if you need to create a snaplock 
        aggregate, the suggested method is to specify -SnapLockType as "compliance" or "enterprise" and not specify 
        -Snaplock at all.
    .Parameter SnapLockType
        Specifies the type of the SnapLock aggregate to be created. Possible values - "compliance" or "enterprise" 
     
        NOTE:     ESERVICENOTLICENSED is returned if the necessary Snaplock compliance or enterprise license has not been installed.
                 EINVALIDINPUT is returned if -SnapLockType has an illegal value.
                 EONTAPI_EWORMNOCLOCK is returned if SnapLock Compliance Clock is not running. 
    .Parameter Server
        NaServer to query
    .Example
        # Create a New Aggregate named aggr1 using four 146GB 15K FCAL disks.
        PS > New-NaAggr -Name aggr1 -DiskCount 4 -DiskSize 146 -DiskType FCAL -RPM 15000
    .Example
        # Create a New Traditional Volume named Vol0 with a Raid group size of 8, and a raid type of RaidDP.
        PS > New-NaAggr -Name Vol0 -DiskCount 4 -RaidSize 8 -RaidType raid_dp -Traditional
    .Example
        # Create a New aggregate named aggr5 mirrored(two plexes 4disk per plex), and a raid type of RaidDP.
        PS > New-NaAggr -Name aggr5 -DiskCount 8 -RaidType raid_dp -Mirrored
    .Example
        Create a mirrored aggregate named aggr3 using all the disks in shelf 4 for plex0, and plex1 with all the disks from shelf 5.
     
        PS > $disks = Get-NaDisk | ? {$_.shelf -eq 4}
        PS > $Mirrordisk = Get-NaDisk | ? {$_.shelf -eq 5}
        PS > New-NaAggr -Name aggr3 -Disk $disks -MirrorDisks $Mirrordisk
    .Outputs
        NetApp.SDK.NaAggr[]
    .Link
        Get-NaAggr
        Set-NaAggr
        Rename-NaAggr
        Remove-NaAggr
    #>
    [CmdletBinding(SupportsShouldProcess=$true,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="Diskcount")]
    param(
        [Parameter(ParameterSetName="DiskList",Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$true,HelpMessage="Name of the aggregate to create.")]
        [Parameter(ParameterSetName="Diskcount",Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$true,HelpMessage="Name of the aggregate to create.")]
        [Alias("Aggregate")]
        [STRING]
        $Name,
    
        [Parameter(ParameterSetName="DiskList",Mandatory=$TRUE,HelpMessage="Specific list of disks to use for the new aggregate.")]
        [string[]]
        $Disk,
    
        [Parameter(ParameterSetName="Diskcount",Mandatory=$TRUE,HelpMessage="Number of disks to add, including the parity disks.")]
        [INT]
        $DiskCount,
    
        [Parameter(ParameterSetName="Diskcount",HelpMessage="The disk size in GB.")]
        [INT]
        $DiskSize,
    
        [Parameter(ParameterSetName="Diskcount",HelpMessage="Type of disks to use: ATA, EATA, FCAL, LUN, SAS, SATA, SCSI, XATA, or XSAS")]
        [ValidateSet("ATA", "EATA", "FCAL", "LUN", "SAS", "SATA", "SCSI", "XATA", "XSAS")]
        [STRING]
        $DiskType,
    
        [Parameter(ParameterSetName="Diskcount",HelpMessage="Rotational speed of disks in revolutions per minute.")]
        [ValidateSet(5400,7200,10000,15000)]
        [INT]
        $Rpm,
    
        [Parameter(HelpMessage="Specifies the maximum number of disks in each RAID group in the aggregate.")]
        [ValidateRange(2, 28)]
        [INT]
        $RaidSize,
    
        [Parameter(HelpMessage="specifies the type of RAID groups to use in the new aggregate.")]
        [ValidateSet("raid4","raid_dp")]
        [STRING]
        $RaidType,
    
        [Parameter(ParameterSetName="DiskList",HelpMessage="Disks in a plex are not normally permitted to span spare pools. This behavior is overridden with this option")]
        [switch]
        $Force,
    
        [Parameter(HelpMessage="Specifies that the new aggregate be mirrored (have two plexes).")]
        [switch]
        $Mirrored,
    
        [Parameter(ParameterSetName="DiskList",HelpMessage="List of mirror disks to use.")]
        [string[]]
        $MirrorDisks,
    
        [Parameter(HelpMessage="Specifies the creation of a SnapLock aggregate.")]
        [switch]
        $Snaplock,
    
        [Parameter(HelpMessage="Specifies the type of Snaplock aggregate to be created.")]
        [ValidateSet("compliance","enterprise")]
        [STRING]
        $SnaplockType,
    
        [Parameter(HelpMessage="Create a traditional volume (aggregate contains exactly one traditional volume)")]
        [switch]
        $Traditional,
        
        [Parameter(HelpMessage="Specifies the language to use for the new traditional volume via a language code.")]
        [ValidateSet("C","da","de","en","en_US","es","fi","fr","he","it","ja","ja_JP.PCK","ko","no","nl","pt","sv","zh","zh.GBK","zh_TW","zh_TW.BIG5")]
        [STRING]
        $LanguageCode,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    Process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-create")
        switch ($PSCmdlet.ParameterSetName){
            "DiskCount"
            {
                $request.AddNewChild("disk-count",$DiskCount)
                if ($DiskSize){
                    $request.AddNewChild("disk-size",(($DiskSize * 1gb)/4kb))
                }
                if ($DiskType){
                    $request.AddNewChild("disk-type",$DiskType)
                }
                if ($Rpm) {
                    $request.AddNewChild("rpm",$Rpm)
                }
                if ($Mirrored){
                    $request.AddNewChild("is-mirrored",$TRUE)
                    if ($Force){
                        $request.AddNewChild("force-spare-pool",$TRUE)
                    }
                }
            }
            "DiskList" 
            {
                $d = New-Object Netapp.Manage.NaElement("disks")
                $Disk|%{
                    $di = New-Object Netapp.Manage.NaElement("disk-info")
                    $di.AddNewChild("name",$_)
                    $d.AddChildElement($di)
                }
                $request.AddChildElement($d)
                if ($MirrorDisks){
                    $m = New-Object Netapp.Manage.NaElement("mirror-disks")
                       $Disk|%{
                        $mi = New-Object Netapp.Manage.NaElement("disk-info")
                        $mi.AddNewChild("name",$_)
                        $m.AddChildElement($mi)
                    }
                    $request.AddChildElement($m)
                    $request.AddNewChild("is-mirrored",$TRUE)
                }
            }
        }
        if ($RaidSize){
            $request.AddNewChild("raid-size",$RaidSize)
        }
        if ($RaidType){
            $request.AddNewChild("raid-type",$RaidType)
        }
        if ($SnaplockType){
            $request.AddNewChild("snaplock-type",$SnaplockType)
        } 
        elseif ($Snaplock){
            $request.AddNewChild("is-snaplock",$TRUE)
        }
        if ($Traditional){
            $request.AddNewChild("type","trad")
            if ($LanguageCode){
                $request.AddNewChild("language-code",$LanguageCode)
            }
        } 
        else{
            $request.AddNewChild("type","aggr")
        }
        $request.AddNewChild("aggregate",$name)
        if ($pscmdlet.ShouldProcess($Server.Server,"Creating new aggregate: $name")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
                write-output (Get-NaAggr -Name $name -server $server)
            }
        }
    }
}
function Test-NaSparesLow {
    <#
    .Synopsis
        Return true if there is no suitable spare disk available for any filesystem (parity or data) disk.
    .Description
           Return true if there is no suitable spare disk available for any filesystem (parity or data) disk.
    .Parameter Server
        NaServer to query
    .Example
           PS > Test-NaSparesLow
           false
    .Outputs
        System.Bool
    .Link
        Get-NaAggr
        Set-NaAggr
        Remove-NaAggr
        Add-NaDisk
    #>
    [CmdletBinding(SupportsShouldProcess=$false,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipeline=$True,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
        Write-Output (ConvertFrom-NaBool $server.Invoke("aggr-check-spare-low").getchildcontent("is-spare-low"))
    }
}
function Get-NaRootVol {
    <#
    .Synopsis
           Return the name of the "root" volume on the filer.
    .Description
         Return the name of the "root" volume on the filer.
    .Parameter Server
           NaServer to query
    .Example
          PS > Get-NaRootVol
          vol0
    .Outputs
        System.String
    .Link
        Get-NaAggr
        Set-NaAggr
        Remove-NaAggr
    .Notes 
        NAME:     Get-NaRootVol
        AUTHOR:   Glenn Sizemore
        #Requires -Version 2.0 
    #>
    [CmdletBinding(SupportsShouldProcess=$false,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipeline=$True,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    Process{
        Write-Output $server.Invoke("aggr-get-root-name").getchildcontent("root-volume")
    }
}

Function New-NaMirror {
    <#
    .Synopsis
        Mirror the specified aggregate/traditional volume.
    .Description
        Turns an unmirrored aggregate/traditional volume into a mirrored aggregate/traditional volume
        by adding a plex to it. The plex is either newly formed from disks chosen from a spare pool 
        or, if the -VictimAggr|-VictimVol option is specified, taken from another existing unmirrored
        aggregate/volume. The named aggregate/volume must currently be unmirrored. Disks may be specified
        explicitly using the -Disk option. The number of disks indicated must match the number present
        in the existing aggregate/traditional volume. if disks are not specified explicitly, then disks
        are automatically selected to match those in the aggregate's/traditional volumes existing plex. 
    .Parameter Aggregate
        Name of the existing aggregate to be mirrored.  
    .Parameter Volume
        Name of the existing volume to be mirrored.  
    .Parameter VictimAggr
        The "victim" aggregate/volume to cannibalize in order to mirror the named aggregate. The "victim-aggregate"
        must have been previously joined with the aggregate to be mirrored and then split from it. The 
        resulting mirrored aggregate is otherwise identical to the original aggregate before the operation.
        The "victim-aggregate" (and all its volumes) is effectively destroyed. 
    
        Note: the victim aggregate (and all its volumes) must be offline.
    .Parameter Disk
        Specific list of mirror disks to add. This list must match the number of disks present on the 
        existing aggregate. The specified disks are not permitted to span disk pools unless overridden
        with the "force-spare-pool" option. 

        To create a list of disk use Get-NaDisk.  The following example will select 5 disks from shelf 2
        PS > $disks = Get-NaDisk -spares | where-object {$_.shelf -eq 2} | select -first 5
    .Parameter ForceSparePool
        Disks in a plex are not normally permitted to span spare pools. This behavior is overridden with this option.
    .Parameter Server
        NaServer to query
    .Example
        # Create a new plex, mirroring aggr1.
        PS > $disks = Get-NaDisk -spares | select -First 5
        PS > New-NaMirror -Name aggr1 -Disk $disks
    .Example
        # Rejoin aggr2 backinto the aggr1 mirror after a split operation has been performed.
        PS > Get-NaAggr aggr1 | New-NaMirror -victimaggr aggr2
    .Outputs
        NetApp.SDK.NaAggr
    .Link
        Get-NaAggr
        Split-NaMirror
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="Disklist")]
    param(
        [Parameter(ParameterSetName="Disklist",Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the existing aggregate to be mirrored.")]
        [Parameter(ParameterSetName="victim",Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the existing aggregate to be mirrored.")]
        [Alias("Aggregate","Volume")]
        [STRING]
        $Name,
    
        [Parameter(ParameterSetName="victim",Mandatory=$TRUE,HelpMessage="The victim aggregate/traditional volume to cannibalize in order to mirror the named aggregate.")]
        [string]
        $VictimAggr,
    
        [Parameter(ParameterSetName="Disklist",Mandatory=$TRUE,HelpMessage="Specific list of mirror disks to add. This list must match the number of disks present on the existing aggregate.")]
        [string[]]
        $Disk,
    
        [Parameter(ParameterSetName="Disklist", HelpMessage="Force the mirroring operation through, past the normal roadblocks that would otherwise cause it to be aborted.")]
        [switch]
        $force,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-mirror")
        $request.AddNewChild("aggregate",$Name)        
        switch ($PSCmdlet.ParameterSetName){
            "victim"
            {
                $request.AddNewChild("victim-aggregate",$VictimAggr)
            }
            "DiskList" 
            {
                $d = New-Object Netapp.Manage.NaElement("mirror-disks")
                $Disk|%{
                    $di = New-Object Netapp.Manage.NaElement("disk-info")
                    $di.AddNewChild("name",$_)
                    $d.AddChildElement($di)
                }
                $request.AddChildElement($d)
                if ($force){
                    $request.AddNewChild("force",$TRUE)
                }
            }
        }      
        if ($pscmdlet.ShouldProcess($server.Server,"Mirroring aggregate $Name")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
	            return Get-NaAggr $name -Server $Server
			}
        }
    }
}
Function Split-NaMirror {
    <#
    .Synopsis
        Remove the specified plex from a mirrored aggregate and create a new unmirrored aggregate.
    .Description
        Remove the specified plex from a mirrored aggregate and create a new unmirrored aggregate with the 
        specified name that contains the plex. The original mirrored aggregate thus becomes unmirrored. The
        plex to be split from the original aggregate must be functional (not partial), but it can otherwise
        be inactive, resyncing, or out-of-date. "Aggr split" can therefore be used to gain access to a plex
        that isn't up to date with respect to its partner plex if its partner plex is currently failed. If 
        the plex is offline at the time of the split, the resulting aggregate will be offline. Otherwise, 
        the resulting aggregate will be in the same online/offline/restricted state as the original aggregate.
        A split mirror can be joined back together via the -VictimAggr option to New-Mirror cmdlet.
    .Parameter Path
        Path of the plex to split out of its aggregate, using the following format: [/vol/][][] 
    .Parameter Name
        Name of the new aggregate to create from the split plex.
    .Parameter Server
        NaServer to query
    .Example
        # Split the mirrored aggregate Aggr1.  Split plex1 off, creating a new aggr named aggr2.
    
        PS > $aggr = Get-NaAggr aggr1
        PS > $aggr.Plexes.plex2 | Split-NaMirror -name aggr2
    .Example
        # Split the mirrored aggregate Aggr1.  Split plex1 off, creating a new aggr named aggr2.
    
        PS > $aggr = Get-NaAggr aggr1
        PS > $aggr.Plexes.Plex1.Path
        /vol/aggr1/plex1
        PS > Split-NaMirror -Path "/vol/aggr1/plex1" -name aggr2
    .Outputs
        NetApp.SDK.NaAggr[]
    .Link
        Get-NaAggr
        Mirror-NaMirror
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,Mandatory=$TRUE,HelpMessage="Name of the new aggregate to create from the split plex.")]
        [STRING]
        $Name,
        
        [Parameter(Position=1,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the plex to split out of its aggregate, using the following format: [/vol/][][] ")]
        [STRING]
        $Path,
    
        [Parameter(Position=2,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-split")
        $request.AddNewChild("aggregate",$path)
        $request.AddNewChild("new-aggr-name",$Name)
        $source = $Path.split("/")[2]
        if ($pscmdlet.ShouldProcess($source,"Splitting $($Name) off")) {
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
	            $source,$Name | select @{n='Name';e={$_}}| Get-NaAggr -Server $Server
			}
        }
    }
}

function Get-NaAggrFilerInfo {
    <#
    .Synopsis
        Get information on what possibilities and parameters exist for aggregates on a given filer.
    .Description
        Get information on what possibilities and parameters exist for aggregates on a given filer.
    .Parameter Server
        NaServer to query
    .Example
        Get-NaAggrFilerInfo
     .Outputs
        NetApp.SDK.NaAggrFilerInfo
    .Link
        Get-NaAggr
    #>
    [CmdletBinding(SupportsShouldProcess=$False,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipeline=$True,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
		try {
			$results = ([xml]$server.Invoke("aggr-get-filer-info")).results
		}
		catch [NetApp.Manage.NaApiFailedException]{
			Write-Warning $_.Exception.GetBaseException().message
			continue;
		}
		if ($?){		
		    $RG = New-Object HashTable
			$results."raidgroup-size"."raidgroup-size-info" | % {
				$RT = New-Object NetApp.SDK.NaRaidGroupInfo -ArgumentList @(
					$_."raidtype",
					$_."default-size",
					$_."maximum-size",
					$_."minimum-size"
				)
				$RG.Add($_."raidtype",$RT)
			}
			return New-Object NetApp.SDK.NaAggrFilerInfo -ArgumentList @(
				[array]($results."allowed-raidtypes"."raidtype-info".raidtype),
				$results."checksum-types",
				$results."default-raidtype",
				$results."disk-types",
				$results."max-named-disks-per-request",
				$RG,
				$results."root-volume",
				$results."snapshots-max"
			)
		}
    }
}
Function Get-NaAggrOptions {
    <#
    .Synopsis
           Get the options that have been set for the specified aggregate.
    .Description
          Get the options that have been set for the specified aggregate.
    .Parameter Name
        Name of the aggregate whose options we want. 
    .Parameter Server
           NaServer to query
    .Example
         # Get the options for aggr1 on the target filer.
        PS > Get-NaAggrFilerInfo aggr1
    .Outputs
        PSObject[]
    .Link
        Get-NaAggr
        Set-NaAggrOptions
    #>
    [CmdletBinding(SupportsShouldProcess=$False,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the aggregate whose options we want.")]
        [Alias("Aggregate")]
        [STRING]
        $Name,
    
        [Parameter(Position=1,HelpMessage="Name of the option we want.")]
        [STRING]
        $OptionName,
    
        [Parameter(Position=2,HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
		try {
			$request = New-Object NetApp.Manage.NaElement("aggr-options-list-info")
			$request.AddNewChild("aggregate",$Name)
			$results = ([xml]$server.InvokeElem($request)).results
		}
		catch [NetApp.Manage.NaApiFailedException]{
			Write-Warning $_.Exception.GetBaseException().message
			$result = $null
			continue;
		}
		if ($?){
			$results.options."aggr-option-info" | Select-Object @{
				Expression={$Name}
				Name='Aggregate'
			},
			@{
				Expression={$_.name}
				Name='OptionName'
			},
			@{
				Expression={$_.value}
				Name='Optionvalue'
			} | Where-Object {$_.OptionName -match $OptionName}
		}
        
    }
}
Function Set-NaAggrOptions {
    <#
    .Synopsis
           Set the specified option for the given aggregate. 
    .Description
          Set the specified option for the given aggregate. The change remains effective even if the filer is
          rebooted. Some options have values that are numbers, and some have values that are "on" (also 
          expressible as "yes", "true", or "1" ) or "off" (also expressible as "no", "false", or "0"). A 
          mixture of uppercase and lowercase characters may be used when typing an option's value. Note that 
          the "root" option is special in that it doesn't have a corresponding value.
    .Parameter Name
        "fs_size_fixed" (value: "on" | "off")
            Setting this option to "on" causes the file system to remain the same size (and not grow) when
            the mirror is broken on a SnapMirrored aggregate (which MUST be embedded in a traditional volume),
            or when an "aggr add" is performed on it(see add-NaDisk). This option is automatically set to be
            "on" when an aggregate becomes SnapMirrored. It remains "on" after the "snapmirror break" command
            (see Split-NaMirror) is issued for an aggregate embedded in a traditional volume. This option allows
            an embedded aggregate to be SnapMirrored back to the source without needing to add disks to the 
            source aggregate. If the aggregate size is larger than the file system size, turning off this option
            forces the file system to grow to the size of the aggregate. 
        "ignore_inconsistent" (value: "on" | "off")
            This command can only be used in maintenance mode. If this option is set to "on", then the root 
            aggregate may be brought online when booting even if it is marked as inconsistent. The user is 
            cautioned that bringing it online prior to running WAFL_check or wafliron may result in further
            file system inconsistency. 
        "nosnap" (value: "on" | "off")
            Setting this option to "on" disables automatic snapshots on the aggregate. 
        "raid_cv" (value: "on" | "off")
            Setting the option to "off" disables block checksum protection on the aggregate. The default is "on".
            The user is cautioned that turning off the option exposes the filesystems contained in the aggregate
            to inconsistency that could be caused by a misbehaving hardware component in the system. 
        "raid_zoned" (value: "on" | "off")
            Setting the option to "off" disables zoned checksum protection on the aggregate. The default is "on".
            The user is cautioned that turning off the option exposes the filesystems contained in the aggregate
            to inconsistency that could be caused by a misbehaving hardware component in the system. 
        "raidsize" (value: < int >>)
            The maximum size of a RAID group within the aggregate. Changing this option doesn't cause existing RAID
            groups to grow or shrink. Rather, it only affects whether more disks will be added to the last existing 
            RAID group in the future, and how large new RAID groups will be. 
        "raidtype" (value: "raid4" | "raid_dp" | "raid0")
            The type of RAID group used for this aggregate. The "raid4" setting provides one parity disk per RAID 
            group, while "raid_dp" provides two. Changing this option immediately changes the RAID group type for 
            all RAID groups in the aggregate. When upgrading RAID groups from "raid4" to "raid_dp", each RAID group 
            begins a reconstruction onto a spare disk allocated for the second "dparity" parity disk. 
        "resyncsnaptime" (value: < int >)
            Sets the mirror resynchronization snapshot frequency to be the given number of minutes. The default 
            value is 60 (minutes). 
        "root" (value: < none >)
            The specified aggregate is to become the root aggregate for the filer on the next reboot. This option 
            can be used on only one aggregate at any given time. The existing root aggregate will become a non-root 
            aggregate after the reboot. Until the system is rebooted, the original aggregate will continue to show 
            root as an option, and the new root aggregate will show diskroot as an option. In general, the aggregate 
            that has the diskroot option is the one that becomes the root aggregate following the next reboot. The 
            only way to remove the root status of an aggregate is to set it on another aggregate. 
        "snapmirrored" (value : "off")
            If SnapMirror is enabled, the filer auto- matically sets this option to "on". Set this option to "off" 
            with the "snapmirror" (see New-NaMirror)command if SnapMirror should no longer be used to update the 
            mirror. After setting this option to "off", the mirror becomes a regular writable aggregate, and all 
            its volumes are restored to whatever state they were last in. Note that it is not possible to set this 
            option directly through this interface. Rather, it is automatically changed as a side effect of running 
            the appropriate "snapmirror" commands. 
        "snapshot_autodelete" (value: "on" | "off")
            Setting this option to "off" disables automatic snapshot deletion on the aggregate. 
        "thorough_scrub" (value: "on" | "off")
            Setting the option to "on" enables thorough scrub on a block checksum aggregate. That means that a scrub 
            will initialize any zeroed checksum entries that it finds. If there are any checksum entries to be initialized,
            scrub will run slower than normal.  
    .Parameter Value
        The value to set the named option. It may be the NULL/empty value only in the case of the "root" option.
    .Parameter Aggregate
        Name of the aggregate for which we want to set an option. 
    .Parameter Server
           NaServer to query
    .Example
         # Get the NoSnap option for aggr1 on the target filer.
        PS > Set-NaAggrOptions -Name "nosnap" -value "on" -Aggregate aggr1
    .Outputs
        PSObject
    .Link
        Get-NaAggr
        Get-NaAggrOptions
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the option to set.")]
        [STRING]
        $OptionName,
        
        [Parameter(Position=1,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="The value to set the named option.")]
        [STRING]
        $OptionValue,
        
        [Parameter(Position=2,Mandatory=$TRUE,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the aggregate for which we want to set an option. ")]
        [STRING]
        $Aggregate,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-set-option")
        $request.AddNewChild("aggregate",$Aggregate)
        $request.AddNewChild("option-name",$OptionName)
        $request.AddNewChild("option-value",$OptionValue)
        if ($pscmdlet.ShouldProcess($Aggregate, "Setting $OptionName to $OptionValue")){
			try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
	            return Get-NaAggrOptions -Name $Aggregate -OptionName $OptionName -server $Server
			}
        }
    }
}
function Get-NaScrub {
    <#
    .Synopsis
        Get the status of parity scrubbing on the named aggregate, plex, or RAID group.
    .Description
        Get the status of parity scrubbing on the named aggregate, plex, or RAID group. Status includes
        percentage complete and the scrub's suspended status (if appropriate).
    .Parameter Path
        Path of an existing aggregate, plex, or RAID group using the following format: [/vol/][][] If no
        name is given, then status is generated for all RAID groups currently being scrubbed. 
    .Parameter Server
        NaServer to query
    .Example
        # Get the status of plex1 on aggr2
        PS > Get-NaScrub -aggregate '/vol/aggr2/plex1'
    
        # or use the pipeline
        PS > (Get-NaAggr aggr2).plexes.plex1 | Get-NaScrub
    .Outputs
        NetApp.SDK.NaScrubInfo[]
    .Link
        Get-NaAggr
        Start-NaScrub
    #>
    [CmdletBinding(SupportsShouldProcess=$False,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="The aggregate/volume to get status for. If not supplied, get status of all aggregates.")]
        [STRING]
        $Path,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-scrub-list-info")
        $request.AddNewChild("verbose",$TRUE)
        if ($Path){
            $request.AddNewChild("aggregate",$Path)
        }
		try {
			$result = ([xml]$server.InvokeElem($request)).results
		}
		catch [NetApp.Manage.NaApiFailedException]{
			Write-Warning $_.Exception.GetBaseException().message
			continue;
		}
		if ($?){
			$result."scrub-details"."scrub-detail-info" | % {
				return New-Object NetApp.SDK.NaScrubInfo -ArgumentList @(
					(ConvertFrom-NaBool $_."is-suspended"),
					$(if ($_."percentage-complete") { $_."percentage-complete"}else{[int]0}),
					$_."raid-group",
					(ConvertFrom-NaTime $_."last-scrub-timestamp")
				)
			}
		}
    }
}

function Start-NaScrub {
    <#
    .Synopsis
        Start parity scrubbing on the named aggregate, plex, or RAID group.
    .Description
        Start parity scrubbing on the named aggregate, plex, or RAID group. Parity scrubbing compares the data disks
        to the parity disk in a RAID group, correcting the parity disk's contents as necessary. If no name is given,
        then parity scrubbing is started on all online aggregates. if an aggregate name is given, scrubbing is started
        on all RAID groups in the aggregate. if a plex name is given, scrubbing is started on all RAID groups contained
        in the plex. if a RAID group name is given, scrubbing is started only on that group. Use Get-NaScrub to check
        scrubbing status.
    .Parameter Path
        Path of the aggregate, plex, or RAID group to scrub, using the following format: [/vol/][][] 
    .Parameter Server
        NaServer to query
    .Example
        # Get the status of plex1 on aggr2
        PS > Start-NaScrub -path '/vol/aggr1'
    .Outputs
        NetApp.SDK.NaScrubInfo[]
    .Link
        Get-NaAggr
        Get-NaScrub
        Stop-NaScrub
        Suspend-NaScrub
        Resume-NaScrub
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the aggregate, plex, or RAID group for which we are to start parity scrubbing")]
        [string]
        $Path,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-scrub-start")
        if ($Path){
            $request.AddNewChild("aggregate",$Path)
        }
        if ($pscmdlet.ShouldProcess($path,"Starting parity scrub")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
            return Get-NaScrub $Path -server $Server
        }
    }
}
function Stop-NaScrub {
    <#
    .Synopsis
        Stop parity scrubbing on the named aggregate, plex, or RAID group.
    .Description
        Stop parity scrubbing on the named aggregate, plex, or RAID group. If no name is given, stop 
        scrubbing on all RAID groups currently being scrubbed.
    .Parameter Path
        Path of the aggregate, plex, or RAID group for which we are to stop parity scrubbing, using the following format: [/vol/][][] 
    .Parameter Server
        NaServer to query
    .Example
        # Stop parity scrubbing on aggr1
        PS > Stop-NaAggrScrub -path '/vol/aggr1'
    .Outputs
        NetApp.SDK.NaScrubInfo[]
    .Link
        Get-NaAggr
        Get-NaScrub
        Start-NaScrub
        Suspend-NaScrub
        Resume-NaScrub
   #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the aggregate, plex, or RAID group for which we are to stop parity scrubbing")]
        [string]
        $path,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-scrub-stop")
        if ($Path){
            $request.AddNewChild("aggregate",$path)
        }
        if ($pscmdlet.ShouldProcess($path,"Stopping parity scrub")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?){
            	return Get-NaScrub $Path -server $Server
			}
        }
    }
}
Function Suspend-NaScrub {
    <#
    .Synopsis
        Suspend parity scrubbing on the named aggregate, plex, or RAID group.
    .Description
        Suspend parity scrubbing on the named aggregate, plex, or RAID group. If no name is given, 
        suspend scrubbing on all RAID groups currently being scrubbed.
    .Parameter Path
        Path of the aggregate, plex, or RAID group to suspend scrub, using the following format: [/vol/][][] 
    .Parameter Server
        NaServer to query
    .Example
        # Suspend parity scrub for aggr1
        PS > Suspend-NaScrub -path '/vol/aggr1'
    .Outputs
        NetApp.SDK.NaScrubInfo[]
    .Link
        Get-NaAggr
        Get-NaScrub
        Start-NaScrub
        Stop-NaScrub
        Resume-NaScrub
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the aggregate, plex, or RAID group for which we are to suspend parity scrubbing")]
        [string]
        $path,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process {
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-scrub-suspend")
        if ($Path){
            $request.AddNewChild("aggregate",$Path)
        }
        if ($pscmdlet.ShouldProcess($path,"Suspending parity scrub")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?) {
            	return Get-NaScrub $Path -server $Server
			}
        }
    }
}
function Resume-NaScrub {
	<#
    .Synopsis
        Resume parity scrubbing on the named aggregate, plex, or RAID group.
    .Description
        Resume parity scrubbing on the named aggregate, plex, or RAID group. If no name is given, 
        Resume scrubbing on all RAID groups currently being scrubbed.
    .Parameter Path
        Path of the aggregate, plex, or RAID group to resume scrub, using the following format: [/vol/][][] 
    .Parameter Server
        NaServer to query
    .Example
        # resume parity scrub for aggr1
        PS > Resume-NaScrub -aggregate '/vol/aggr1'
    .Outputs
        NetApp.SDK.NaScrubInfo[]
    .Link
        Get-NaAggr
        Get-NaScrub
        Start-NaScrub
        Stop-NaScrub
        Suspend-NaScrub
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Path of the aggregate, plex, or RAID group for which we are to resume parity scrubbing")]
        [string]
        $path,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-scrub-resume")
        if ($path){
            $request.AddNewChild("aggregate",$path)
        }
        if ($pscmdlet.ShouldProcess($path,"Resuming parity scrub")){
            try {
				$server.InvokeElem($request)|out-null
			}
			catch [NetApp.Manage.NaApiFailedException]{
				Write-Warning $_.Exception.GetBaseException().message
				continue;
			}
			if ($?) {
            	return Get-NaScrub $Path -server $Server
			}
        }
    }
}
Function Get-NaVerify {
    <#
    .Synopsis
        Get the status of RAID mirror verification on the named aggregate/volume.
    .Description
        Get the status of RAID mirror verification on the named aggregate/volume. Status includes percentage
        complete and whether it's currently suspended.
    .Parameter Name
        Name of an existing aggregate / volume. If no name is given, then mirror verification status is 
        generated for all aggregates currently being verified. 
    .Parameter Server
        NaServer to query
    .Example
        # Get the status of RAID mirror verification on aggr1
        PS > Get-NaVerify -Aggregate aggr1
    .Outputs
        NetApp.SDK.NaVerifyInfo[]
    .Link
        Get-NaAggr
        Start-NaVerify
        Resume-NaVerify
        Suspend-NaVerify
        Stop-NaVerify
    #>
    [CmdletBinding(SupportsShouldProcess=$False,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="The aggregate/volume to get status for.")]
        [Alias("Aggregate")]
        [string]
        $name,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-verify-list-info")
        if ($name){
            $request.AddNewChild("aggregate",$name)
        }
        trap{
            write-warning "no verifications are running at this time"
            continue;
        }
        $result  = ([xml]$server.InvokeElem($request)).results
        $result."verify-details"."verify-detail-info" | % {
            if ($_.name -ne $null) 
            {
                return New-Object NetApp.SDK.NaVerifyInfo -ArgumentList @(
                    (ConvertFrom-NaBool $_."is-suspended"),
                    $_.name,
                    $_."percentage-complete"
                )
            }
        }
    }
}
Function Start-NaVerify {
    <#
    .Synopsis
        Start RAID mirror verification on the named aggregate/volume. 
    .Description
        Start RAID mirror verification on the named aggregate/volume. Verification compares the data in both plexes of
        a mirrored aggregate. in the default case, any blocks that differ are logged and no changes are made. The
        -FixPlex option is used to fix any mismatches. It specifies which plex to fix. if no name is given, then mirror
        verification is started on all online aggregates. Use the Get-NaVerify cmdlet to check mirror verification status.
        if the -FixPlex option is used, then a name must be specified.
    .Parameter Name
        Name of the mirrored aggregate to verify. 
    .Parameter FixPlex
        If provided, this specifies the plex to fix in case the two plexes do not match. The default is to log any 
        discrepancies instead of fixing them.
    .Parameter ASync
        If provided, then execute the cmd in the background return control to the console. This option only applies if
        -Name is provided.
    .Parameter Server
        NaServer to query
    .Example
        # start RAID mirror verification on aggr1 log only
        PS > Start-NaVerify -Name aggr1
    .Example
        # start RAID mirror verification on aggr1 syncing plex1
        PS > Start-NaVerify -Name aggr1 -fixplex 1
    .Outputs
        NetApp.SDK.NaVerifyInfo[]
    .Link
        Get-NaAggr
        Get-NaVerify
        Resume-NaVerify
        Suspend-NaVerify
        Stop-NaVerify
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the mirrored traditional volume to verify.")]
        [Alias("Volume","Aggregate")]
        [STRING]
        $Name,
    
        [Parameter(Position=1,HelpMessage="If provided, this specifies the plex to fix.")]
        [INT]
        $FixPlex,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-verify-start")
        if ($name){
            $request.AddNewChild("aggregate",$name)
            $target = $name
        } 
        else{
            $target = 'ALL mirrored aggregates'
        }
        if ($FixPlex){
            $request.AddNewChild("fix-plex",$FixPlex)
        }
        if ($pscmdlet.ShouldProcess($target,"Verify RAID Mirror")){
            trap{continue}
            $server.InvokeElem($request)|out-null
            return Get-NaVerify $name -server $Server
        }
    }
}
Function Resume-NaVerify {
    <#
    .Synopsis
        Resume RAID mirror verification on the named aggregate/volume. 
    .Description
        Resume RAID mirror verification on the named aggregate. If no name is given, then resume mirror verification
        on all aggregates that have been suspended.
    .Parameter Name
        Name of the mirrored aggregate/volume to resume verification. 
    .Parameter Server
        NaServer to query
    .Example
        # resume verification on aggr1
        PS >Resume-NaVerify -Aggregate aggr1
    .Outputs
        NetApp.SDK.NaVerifyInfo[]
    .Link
        Get-NaAggr
        Get-NaVerify
        Set-NaVerify
        Suspend-NaVerify
        Stop-NaVerify
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the mirrored aggregate/traditional volume to resume verification.")]
        [Alias("Volume","Aggregate")]
        [STRING]
        $name,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-verify-resume")
        if ($Name){
            $request.AddNewChild("aggregate",$name)
        } 
        else{
            $name = "All Mirrored Aggregates"
        }
        if ($pscmdlet.ShouldProcess($name,"Resume RAID mirror verification")){
            trap{continue}
            $server.InvokeElem($request)|out-null
            Get-NaVerify -Name $name -server $Server
        }
    }
}
function Suspend-NaVerify {
    <#
    .Synopsis
        Suspend RAID mirror verification on the named aggregate/volume. 
    .Description
        Suspend RAID mirror verification on the named aggregate. If no name is given, suspend mirror verification 
        on all aggregates currently being verified.
    .Parameter Name
        Name of the mirrored aggregate to suspend verification. 
    .Parameter Server
        NaServer to query
    .Example
        # suspend verification on aggr1
        PS > Suspend-NaVerify -Aggregate aggr1
    .Outputs
        NetApp.SDK.NaVerifyInfo[]
    .Link
        Get-NaAggr
        Get-NaVerify
        Set-NaVerify
        Resume-NaVerify
        Stop-NaVerify
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="low",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the mirrored traditional volume to suspend verification.")]
        [Alias("Volume","Aggregate")]
        [STRING]
        $name,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-verify-suspend")
        if ($Name){
            $request.AddNewChild("aggregate",$name)
        } 
        else{
            $name = "All Mirrored Aggregates"
        }
        if ($pscmdlet.ShouldProcess($name,"Suspend RAID mirror verification")){
            trap{
                write-warning "no verifications are running at this time"
                continue;
            }
            $server.InvokeElem($request)|out-null
            Get-NaVerify -Name $name -server $Server
        }
    }
}
function Stop-NaVerify {
    <#
    .Synopsis
        Stop RAID mirror verification on the named aggregate/volume. 
    .Description
        Stop RAID mirror verification on the named aggregate. If no name is given, suspend mirror verification 
        on all aggregates currently being verified.
    .Parameter Name
        Name of the mirrored aggregate to Stop verification. 
    .Parameter Server
        NaServer to query
    .Example
        # Stop verification on aggr1
        PS > Stop-NaVerify -Aggregate aggr1
    .Outputs
        NetApp.SDK.NaVerifyInfo[]
    .Link
        Get-NaAggr
        Get-NaVerify
        Set-NaVerify
        Suspend-NaVerify
        Resume-NaVerify
    #>
    [CmdletBinding(SupportsShouldProcess=$True,SupportsTransactions=$False,ConfirmImpact="none",DefaultParameterSetName="")]
    param(
        [Parameter(Position=0,ValueFromPipelineByPropertyName=$TRUE,HelpMessage="Name of the mirrored traditional volume to stop verification.")]
        [Alias("Volume","Aggregate")]
        [STRING]
        $name,
    
        [Parameter(HelpMessage="NaServer to query")]
        [NetApp.SDK.NaServer] 
        $server = (Get-NaServer)
    )
    process{
        $request = New-Object NetApp.Manage.NaElement -ArgumentList @("aggr-verify-stop")
        if ($Name){
            $request.AddNewChild("aggregate",$name)
        } 
        else{
            $name = "All Mirrored Aggregates"
        }
        if ($pscmdlet.ShouldProcess($name,"Stopping RAID mirror verification")){
            trap{continue}
            $server.InvokeElem($request)|out-null
            Get-NaVerify -Name $name -server $Server
        }
    }
}
